#include <QDebug>

#include "FuncTree.h"

#include <QtCore>
//#include <QtCore/QFileInfo>
//#include <QtGui/QAction>
//#include <QtGui/QContextMenuEvent>
//#include <QtGui/QMenu>
#include <QtGui/QLineEdit>
//#include <QtGui/QHeaderView>
#include <QtGui/QPushButton>
#include <QtGui/QTreeWidget>
//#include <QtGui/QTreeWidgetItem>
//#include <QtGui/QVBoxLayout>
#include <QHBoxLayout>

//#include "juffed/CommandStorage.h"
//#include "juffed/Functions.h"
//#include "juffed/TextDocSettings.h"

static const int BtnSize = 24;

class FilterLineEdit : public QWidget {
public:
	FilterLineEdit(QWidget* parent = 0) : QWidget(parent) {
		lineEd_ = new QLineEdit("", this);
		clearBtn_ = new QPushButton(QIcon(":clear"), "", this);
		clearBtn_->setFlat(true);
		clearBtn_->setFocusPolicy(Qt::NoFocus);
		clearBtn_->setCursor(Qt::ArrowCursor);
		clearBtn_->setMaximumWidth(BtnSize);
		
		QHBoxLayout* hBox = new QHBoxLayout(this);
		hBox->setMargin(0);
		hBox->setSpacing(0);
		hBox->addWidget(lineEd_);
#if QT_VERSION >= 0x040500
		lineEd_->setTextMargins(0, 0, BtnSize, 0);
		clearBtn_->setParent(lineEd_);
#else
		hBox->addWidget(clearBtn_);
#endif
	}
	
#if QT_VERSION >= 0x040500
	virtual void resizeEvent(QResizeEvent*) {
		clearBtn_->setGeometry(lineEd_->width() - BtnSize, (lineEd_->height() - BtnSize) / 2, BtnSize, BtnSize);
	}
#endif
	
	QLineEdit* lineEd_;
	QPushButton* clearBtn_;
};

class TreeWidget : public QTreeWidget {
public:
	TreeWidget() : QTreeWidget() {
//		contextMenu_ = new QMenu();
//		contextMenu_->addAction(Juff::CommandStorage::instance()->action(Juff::ID_FILE_SAVE));
//		contextMenu_->addAction(Juff::CommandStorage::instance()->action(Juff::ID_FILE_CLOSE));
	}
	virtual ~TreeWidget() {
//		delete contextMenu_;
	}
	
	virtual void contextMenuEvent(QContextMenuEvent * event) {
//		contextMenu_->popup(event->globalPos());
	}
	
//	QMenu* contextMenu_;
};

class FuncTree::Panel : public QWidget {
Q_OBJECT
public:
	Panel() {
		setWindowTitle(tr("Symbols"));
		
		tree_ = new TreeWidget();
		filter_ = new FilterLineEdit();
		filter_->setMaximumHeight(24);
		pathEd_ = new QLineEdit("", this);
		
		QVBoxLayout* vBox = new QVBoxLayout();
		vBox->setMargin(0);
		vBox->setSpacing(2);
		setLayout(vBox);
		
		goBtn_ = new QPushButton("GO", this);
		vBox->addWidget(pathEd_);
		vBox->addWidget(goBtn_);
		vBox->addWidget(tree_);
		vBox->addWidget(filter_);
		
		pathEd_->setText("/home/mrz/data/src/my/juffed/0.8/src");
		QStringList labels;
		labels << "Symbol" << "Full name";// << "File" << "Line" << "Type";
		tree_->setHeaderLabels(labels);

//		tree_->header()->hide();
//		tree_->setColumnHidden(1, true);
//		tree_->setRootIsDecorated(false);
		
		connect(filter_->lineEd_, SIGNAL(textChanged(const QString&)), SLOT(filterItems(const QString&)));
		connect(filter_->clearBtn_, SIGNAL(clicked()), SLOT(clear()));
	}
	
	TreeWidget* tree_;
	FilterLineEdit* filter_;
	QLineEdit* pathEd_;
	QPushButton* goBtn_;

public slots:
	void filterItems(const QString& text) {
//		int n = tree_->topLevelItemCount();
//		for (int i = 0; i < n; ++i) {
//			QTreeWidgetItem* item = tree_->topLevelItem(i);
//			if ( item->text(0).toLower().contains(text.toLower()) ) {
//				item->setHidden(false);
//			}
//			else {
//				item->setHidden(true);
//			}
//		}
	}
	
	void clear() {
		filter_->lineEd_->setText("");
	}
};

FuncTree::FuncTree() : QObject(), JuffPlugin() {
	panel_ = new Panel();
	
	connect(panel_->tree_, SIGNAL(itemClicked(QTreeWidgetItem*, int)), SLOT(docClicked(QTreeWidgetItem*, int)));
	connect(panel_->tree_, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), SLOT(curItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)));
	connect(panel_->tree_, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), SLOT(onItemDoubleClicked(QTreeWidgetItem*, int)));
	connect(panel_->goBtn_, SIGNAL(clicked()), SLOT(countTags()));
	
	applySettings();
}

FuncTree::~FuncTree() { 
}

QString FuncTree::name() const {
	return "Symbols";
}

QString FuncTree::targetEngine() const {
	return "all";
}

QString FuncTree::description() const {
	return "";
}

QWidgetList FuncTree::dockList() const {
	QWidgetList wList;
	wList << panel_;
	return wList;
}

void FuncTree::onDocCreated(const QString& fileName) {
	//	if document with this fileName already exists, exit
//	if ( !panel_->tree_->findItems(fileName, Qt::MatchFixedString, 1).isEmpty() )
//		return;
//
//	QStringList list; 
//	if ( Juff::isNoname(fileName) )
//		list << tr("Noname") << fileName;
//	else
//		list << QFileInfo(fileName).fileName() << fileName;
//	
//	QTreeWidgetItem* item = new QTreeWidgetItem(list);
//	item->setIcon(0, QIcon(QString(":doc_icon")));
//	item->setToolTip(0, fileName);
//	panel_->tree_->addTopLevelItem(item);
//	panel_->tree_->setCurrentItem(item);
}

void FuncTree::onDocActivated(const QString& fileName)  {
//	QList<QTreeWidgetItem*> items = panel_->tree_->findItems(fileName, Qt::MatchFixedString, 1);
//	if ( !items.isEmpty() )
//		panel_->tree_->setCurrentItem(items[0]);
}

void FuncTree::onDocModified(const QString& fileName, bool modified) {
//	QList<QTreeWidgetItem*> items = panel_->tree_->findItems(fileName, Qt::MatchFixedString, 1);
//
//	foreach (QTreeWidgetItem* item, items) {
//		QString fName = Juff::getDocTitle(fileName);
//		if ( modified )
//			fName += "*";
//
//		item->setText(0, fName);
//	}
}

void FuncTree::onDocClosed(const QString& fileName) {
//	QList<QTreeWidgetItem*> items = panel_->tree_->findItems(fileName, Qt::MatchFixedString, 1);
//
//	foreach (QTreeWidgetItem* item, items) {
//		delete item;
//	}
}

void FuncTree::onDocRenamed(const QString& oldFileName, const QString& newFileName) {
//	QList<QTreeWidgetItem*> items = panel_->tree_->findItems(oldFileName, Qt::MatchFixedString, 1);
//
//	foreach (QTreeWidgetItem* item, items) {
//		item->setText(0, Juff::getDocTitle(newFileName));
//		item->setText(1, newFileName);
//		item->setToolTip(0, newFileName);
//	}
}

void FuncTree::onTabMoved(int from, int to) {
//	QTreeWidgetItem* item = panel_->tree_->takeTopLevelItem(from);
//	if ( item ) {
//		panel_->tree_->insertTopLevelItem(to, item);
//	}
}

void FuncTree::curItemChanged(QTreeWidgetItem* it, QTreeWidgetItem*) {
//	if ( it )
//		manager()->openDoc(it->text(1));
}

void FuncTree::applySettings() {
//	QPalette plt = panel_->tree_->palette();
//	plt.setColor(QPalette::Base, TextDocSettings::defaultBgColor());
//	plt.setColor(QPalette::Text, TextDocSettings::defaultFontColor());
//	
//	QColor selBgColor = TextDocSettings::selectionBgColor();
//	plt.setColor(QPalette::Highlight, selBgColor);
//	if ( selBgColor.red() + selBgColor.green() + selBgColor.blue() < 3 * 255 / 2)
//		plt.setColor(QPalette::HighlightedText, Qt::white);
//	else
//		plt.setColor(QPalette::HighlightedText, Qt::black);
//
//	panel_->tree_->setPalette(plt);
//	panel_->filter_->setPalette(plt);
}

enum {
	RoleFileName = Qt::UserRole + 1,
	RoleLineNumber,
	RoleType,
	RoleNamespace
};

QString findAttribute(const QStringList& list, const QString& attr, int startIndex = 0) {
	for (int i = startIndex; i < list.count(); ++i) {
		const QString& item = list[i];
		if ( item.startsWith(attr) ) {
			return item.section(':', 1, -1).simplified();
		}
	}
	return "";
}

void FuncTree::countTags() {
	QString path = panel_->pathEd_->text();
	qDebug() << path;
	
	panel_->tree_->clear();
	
	QString fileName("project_tags_list");
	QProcess proc;
	proc.start("ctags", QStringList() << "-R" << "--excmd=number" << "-f" << fileName << path);
	proc.waitForFinished();
	
//	QMap<QString, QTreeWidgetItem*> classItems;
	QMap<QString, QList<QTreeWidgetItem*> > classItemsBuffer;
	
	QFile file(fileName);
	if (file.open(QIODevice::ReadOnly)) {
		QString line;
		while (!file.atEnd()) {
			line = file.readLine();
			if (!line.isEmpty() && line[0] != '!') {
				QStringList parts = line.split('\t');
				if (parts.count() < 4)
					continue;
				
				QString name = parts[0];
				QString file = QDir(path).absoluteFilePath(parts[1]);
				int line = parts[2].section(";", 0, 0).toInt();
				QString type = parts[3];
				if ( type == "c" ) {
					// this is a class
					qDebug() << "Class" << parts[0];
					
					// store the full class name (including namespace)
					QString fullClassName;
					QString outerClass = findAttribute(parts, "class", 4);
					if ( !outerClass.isEmpty() ) {
						fullClassName = outerClass + "::" + name;
					}
					else {
						QString nameSp = findAttribute(parts, "namespace", 4);
						fullClassName = nameSp.isEmpty() ? name : nameSp + "::" + name;
					}
					QStringList elements;
					elements << name << fullClassName;
					
					QTreeWidgetItem* item = new QTreeWidgetItem(elements);
					item->setData(0, RoleFileName, file);
					item->setData(0, RoleLineNumber, line);
					item->setData(0, RoleType, type);

					panel_->tree_->addTopLevelItem(item);

					QList<QTreeWidgetItem*> items = panel_->tree_->findItems(fullClassName, Qt::MatchFixedString, 1);
					if ( !items.isEmpty() && classItemsBuffer.contains(fullClassName) ) {
						// member functions of this class were already found
						QTreeWidgetItem* classItem = items[0];
						QList<QTreeWidgetItem*>& list = classItemsBuffer[fullClassName];
						foreach (QTreeWidgetItem* item, list) {
							classItem->addChild(item);
						}
						classItemsBuffer.remove(fullClassName);
					}
				}
				else if ( type == "f" ) {
					qDebug() << "Function" << parts[0];
					// check if there are additional info
					if ( parts.count() > 4 ) {
						qDebug() << "    Has additional info";
						
						QString className = findAttribute(parts, "class", 4);
						if ( !className.isEmpty() ) {
							QList<QTreeWidgetItem*> items = panel_->tree_->findItems(className, Qt::MatchFixedString, 1);
							QTreeWidgetItem* item = new QTreeWidgetItem(QStringList() << name << className);
							item->setData(0, RoleFileName, file);
							item->setData(0, RoleLineNumber, line);
							item->setData(0, RoleType, type);
							if ( !items.isEmpty() ) {
								// there is also the item for this class
								qDebug() << "Have a class";
								items[0]->addChild(item);
							}
							else {
								// no such item yet - store for now
								qDebug() << "Storing for later use";
								classItemsBuffer[className] << item;
							}
						}
						else {
							// not a class member - TODO
						}
					}
				}
			}
		}
		file.close();
		file.remove();
	}
}

void FuncTree::onItemDoubleClicked(QTreeWidgetItem* item, int) {
	if ( 0 == item )
		return;
	
	QString fileName = item->data(0, RoleFileName).toString();
	QString type = item->data(0, RoleType).toString();
	bool ok;
	int line = item->data(0, RoleLineNumber).toInt(&ok) - 1;
	if ( !ok || fileName.isEmpty() )
		return;
	manager()->openDoc(fileName);
	manager()->setCursorPos(line, 0);
}

#include "FuncTree.moc"

Q_EXPORT_PLUGIN2(functree, FuncTree)
